package jwt

import (
	"errors"
	"github.com/dgrijalva/jwt-go"
	"time"
)

const SIGNKEY = "12356"

var (
	TokenExpired   error = errors.New("Token is expired")
	TokenMalformed error = errors.New("That's not even a token")
	TokenInvalid   error = errors.New("Couldn't handle this token:")
)

type CustomClaims struct {
	Uid      string   `json:"uid"`
	Username string   `json:"username"`
	Role     []string `json:"role"`
	jwt.StandardClaims
}

func NewCustomClaims(uid, name string, role []string) *CustomClaims {
	return &CustomClaims{
		Uid:      uid,
		Username: name,
		Role:     role,
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 1000,
			ExpiresAt: time.Now().Unix() + 3600000,
		},
	}
}

type JWTService interface {
	CreateToken(claims CustomClaims) (string, error)
	ParseToken(tokenString string) (*CustomClaims, error)
}

type defaultJWTService struct {
	SigningKey []byte
}

func NewJWTService(signKey string) JWTService {
	return &defaultJWTService{
		[]byte(signKey),
	}
}

func (j *defaultJWTService) CreateToken(claims CustomClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

func (j *defaultJWTService) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if token == nil {
		return nil, TokenInvalid
	}
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
				return nil, TokenExpired
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, TokenInvalid
}
